﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataCore
{
    [Serializable]
    public abstract class DomainObject<IdT>
    {
        /// <summary>
        /// ID may be of type string, int, custom type, etc.
        /// Setter is protected to allow unit tests to set this property via reflection and to allow 
        /// domain objects more flexibility in setting this for those objects with assigned IDs.
        /// </summary>
        public virtual IdT ID { get; protected set; }

        ///<summary>
        ///Determines whether the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />.
        ///</summary>
        ///
        ///<returns>
        ///true if the specified <see cref="T:System.Object" /> is equal to the current <see cref="T:System.Object" />; otherwise, false.
        ///</returns>
        ///
        ///<param name="obj">The <see cref="T:System.Object" /> to compare with the current <see cref="T:System.Object" />. </param>
        ///<exception cref="T:System.NullReferenceException">The <paramref name="obj" /> parameter is null.</exception><filterpriority>2</filterpriority>
        public override sealed bool Equals(object obj)
        {
            var compareTo = obj as DomainObject<IdT>;

            return (compareTo != null) &&
                   (HasSameNonDefaultIdAs(compareTo) ||
                // Since the IDs aren't the same, either of them must be transient to 
                // compare business value signatures
                    (((IsTransient()) || compareTo.IsTransient()) &&
                     HasSameBusinessSignatureAs(compareTo)));
        }

        public static bool operator ==(DomainObject<IdT> obj1, DomainObject<IdT> obj2)
        {
            return Equals(obj1, obj2);
        }

        public static bool operator !=(DomainObject<IdT> obj1, DomainObject<IdT> obj2)
        {
            return !(obj1 == obj2);
        }

        /// <summary>
        /// Transient objects are not associated with an item already in storage. 
        ///  </summary>
        public bool IsTransient()
        {
            return ID == null || ID.Equals(default(IdT));
        }

        /// <summary>
        /// Must be provided to properly compare two objects
        /// </summary>
        public abstract override int GetHashCode();

        private bool HasSameBusinessSignatureAs(DomainObject<IdT> compareTo)
        {
            //Check.Require(compareTo != null, "compareTo may not be null");

            return GetHashCode().Equals(compareTo.GetHashCode());
        }

        public abstract void CopyFrom(DomainObject<IdT> T);

        /// <summary>
        /// Returns true if self and the provided persistent object have the same ID values 
        /// and the IDs are not of the default ID value
        /// </summary>
        private bool HasSameNonDefaultIdAs(DomainObject<IdT> compareTo)
        {
            //Check.Require(compareTo != null, "compareTo may not be null");

            return (ID != null && !ID.Equals(default(IdT))) &&
                   (compareTo.ID != null && !compareTo.ID.Equals(default(IdT))) &&
                   ID.Equals(compareTo.ID);
        }
    }
}
